package com.yaoandw.util;

public class Base64Encode {
	/**
	 * Mapping table.
	 */
	private static byte[] checkup = { (byte) 'A', (byte) 'B', (byte) 'C',
			(byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H',
			(byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M',
			(byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R',
			(byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W',
			(byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b',
			(byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g',
			(byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
			(byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q',
			(byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v',
			(byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0',
			(byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
			(byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+',
			(byte) '/', (byte) '=' };

	/**
	 * Returns an integer from a printable character.
	 */
	private static int check(byte b) throws Exception {
		if (b >= 'A' && b <= 'Z') {
			return b - 'A';
		}
		if (b >= 'a' && b <= 'z') {
			return b - 'a' + 26;
		}
		if (b >= '0' && b <= '9') {
			return b - '0' + 52;
		}
		if (b == '+') {
			return 62;
		}
		if (b == '/') {
			return 63;
		}
		// if(b=='\n') return -2;
		// return -1;
		throw new Exception("Incorrect base64 coding");
	}

	private static byte[] encode64(byte[] input) { // 真正进行字符串转码在此处
		if (input == null || input.length == 0) {
			return null;
		}

		int temp, t;
		int length = input.length;
		int size = (length / 3) * 4;

		if (length != (size / 4) * 3) {
			size += 4;
		}
		byte[] output = new byte[size];
		int offset = 0;
		int i, j;

		for (i = 3; i <= length; i += 3) {
			temp = input[i - 3] & 0xff;
			temp <<= 8;
			temp |= input[i - 2] & 0xff;
			temp <<= 8;
			temp |= input[i - 1] & 0xff;
			for (j = 3; j >= 0; j--) {
				t = temp & 0x3f;
				output[offset + j] = checkup[t];
				temp >>>= 6;
			}
			offset += 4;
		}

		if (length - i + 3 == 1) {
			t = (input[i - 3] & 0xfc) >>> 2;
			output[offset++] = checkup[t];
			t = (input[i - 3] & 0x03) << 4;
			output[offset++] = checkup[t];
			output[offset++] = checkup[64];
			output[offset++] = checkup[64];
		} else if (length - i + 3 == 2) {
			t = (input[i - 3] & 0xfc) >>> 2;
			output[offset++] = checkup[t];
			t = (input[i - 3] & 0x03) << 4;
			t |= (input[i - 2] & 0xf0) >>> 4;
			output[offset++] = checkup[t];
			t = (input[i - 2] & 0x0f) << 2;
			output[offset++] = checkup[t];
			output[offset++] = checkup[64];
		}
		return output;
	}

	/**
	 * Encodes a byte array
	 */
	public static byte[] encode(byte[] input) {
		return encode64(input);
	}

	/**
	 * Encodes a string
	 * 
	 * @param input
	 * @return
	 */
	public String encode(String input) {
		return new String(encode64(input.getBytes()));
	}

	private static byte[] decode64(byte[] input) { // 从base64编码恢复字符串
		if (input == null || input.length == 0) {
			return null;
		}

		int temp, t, pad = 0;
		int length = input.length;
		int size = (length / 4) * 3;

		if (input == null || length == 0 || length != (size / 3) * 4) {
			return null;
		}
		if (input[length - 2] == checkup[64]) {
			pad = 4; // padding bits in the last byte
			size -= 2;
		} else if (input[length - 1] == checkup[64]) {
			size--;
			pad = 2;
		}

		byte[] output = new byte[size];
		int offset = 0;
		int i, j;

		try {

			for (i = 4; i <= length - pad; i += 4) {
				temp = 0;
				temp |= check(input[i - 4]);
				temp <<= 6;
				temp |= check(input[i - 3]);
				temp <<= 6;
				temp |= check(input[i - 2]);
				temp <<= 6;
				temp |= check(input[i - 1]);
				for (j = 2; j >= 0; j--) {
					output[offset + j] = (byte) (temp & 0xff);
					temp >>>= 8;
				}
				offset += 3;
			}

			if (pad == 4) {
				t = (check(input[i - 4]) & 0x3f) << 2;
				t |= (check(input[i - 3]) & 0x30) >>> 4;
				output[offset++] = (byte) t;
			} else if (pad == 2) {
				t = (check(input[i - 4]) & 0x3f) << 2;
				t |= (check(input[i - 3]) & 0x30) >>> 4;
				output[offset++] = (byte) t;
				t = (check(input[i - 3]) & 0x0f) << 4;
				t |= (check(input[i - 2]) & 0x3c) >>> 2;
				output[offset++] = (byte) t;
			}

		} catch (Exception e) {
			return null;
		}
		return output;
	}

	/**
	 * Decodes a byte array
	 */
	public static byte[] decode(byte[] input) {
		return decode64(input);
	}

	/**
	 * Decodes a string
	 * 
	 * @param input
	 * @return
	 */
	public static String decode(String input) {
		return new String(decode64(input.getBytes()));
	}
}
